<?php
/**
 * Smart Library - Smart Framework
 * http://smart-framework.highet.eu
 *
 * @category   SmartL
 * @package    SmartL_DataBinder
 * @copyright  Copyright (c) 2005-2008 Smart Framework team - Stepan Sindelar, Vojtech Vit
 * @license    http://www.opensource.org/licenses/bsd-license.php	New BSD License
 */

/**
 * @see Zend_Loader_PluginLoader
 */
require_once 'Zend/Loader/PluginLoader.php';

/**
 * @see SmartL_DataBinder_BindingRules
 */
require_once 'SmartL/DataBinder/BindingRules.php';

/**
 * @see SmartL_DataBinder_BindingRule
 */
require_once 'SmartL/DataBinder/BindingRule.php';

/**
 * Class for data binding between 2 objects.
 * These objects are usually UI object (eg. Zend_Form) and Business object (eg. Doctrine_Record, Doctrine_Query, ...),
 * and so they are called UIObject and BusinessObject, for easier resolution of them.
 *
 * <?php
 *		$query = new Doctrine_Query();
 *		SmartL_DataBinder::updateData(  $form, 'zendForm',  $query, new SmartL_DataBinder_DataAdapter_DoctrineQuery('Db_Record_RecordName','update'), array('username'=>'userName','address_street'=>array('address','street')) );
 *		$query->execute();
 * ?>
 *
 * @todo 24: Otestovat, dokoncit, přehodnotit DataBinding ve SmartL a jeho případný přínos.
 * @package SmartL_DataBinder
 */
class SmartL_DataBinder {
	/**
	 * Binding associations
	 *
	 * @var array[string businessObjectPropertyName|numeric]=>UIObjectPropertyName
	 */
	private $_rules;
	
	/**
	 * @var SmartL_DataBinder_DataAdapter_Interface
	 */
	private $_businessObjectDataAdapter = null;
	
	/**
	 * @var SmartL_DataBinder_DataAdapter_Interface
	 */
	private $_UIObjectDataAdapter = null;
	
	/**
	 * @var Zend_Loader_PluginLoader
	 */
	private $_loader = null;
	
	public function __construct($businessObject = null, $businessObjectDataAdapter = null, $UIObject = null, $UIObjectDataAdapter = null, $bindingRules = array()) {
		$this->_loader = new Zend_Loader_PluginLoader ( );
		$this->_loader->addPrefixPath ( 'SmartL_DataBinder_DataAdapter', 'SmartL' . DIRECTORY_SEPARATOR . 'DataBinder' . DIRECTORY_SEPARATOR . 'DataAdapter' . DIRECTORY_SEPARATOR );
		
		if ($businessObject !== null) {
			$this->setBusinessObject ( $businessObject, $businessObjectDataAdapter );
		}
		if ($UIObject !== null) {
			$this->setUIObject ( $UIObject, $UIObjectDataAdapter );
		}
		$this->setBindingRules ( $bindingRules );
	}
	
	public function setBusinessObject($object, $adapter) {
		$this->_businessObjectDataAdapter = $this->_getAdapter ( $adapter );
		$this->_businessObjectDataAdapter->setObject ( $object );
	}
	public function getBusinessObject() {
		return $this->_businessObjectDataAdapter->getObject ();
	}
	
	public function setUIObject($object, $adapter) {
		$this->_UIObjectDataAdapter = $this->_getAdapter ( $adapter );
		$this->_UIObjectDataAdapter->setObject ( $object );
	}
	public function getUIObject() {
		return $this->_UIObjectDataAdapter->getObject ();
	}
	
	/**
	 * @param string|SmartL_DataBinder_DataAdapter_Interface $adapter
	 * @return SmartL_DataBinder_DataAdapter_Interface
	 */
	private function _getAdapter($adapter) {
		if (is_string ( $adapter )) {
			$className = $this->_loader->load ( ucfirst ( $adapter ) );
			$adapter = new $className ( );
		}
		if (! $adapter instanceof SmartL_DataBinder_DataAdapter_Interface) {
			$name = get_class ( $adapter );
			require_once 'SmartL/DataBinder/Exception.php';
			throw new SmartL_DataBinder_Exception ( "Given DataAdapter {$name} does
			not implement interface SmartL_DataBinder_DataAdapter_Interface" );
		}
		return $adapter;
	}
	
	/**
	 * @param array[numeric| (string|array businessObjectField) ] => string|array UIObjectField | SmartL_DataBinder_BindingRules
	 */
	public function setBindingRules($rules) {
		if ($rules instanceof SmartL_DataBinder_BindingRules) {
			$this->_bindingRules = $rules;
		} else {
			$this->_bindingRules = new SmartL_DataBinder_BindingRules ( $rules );
		}
	}
	
	/**
	 * @return SmartL_DataBinder_BindingRules
	 */
	public function getBindingRules() {
		return $this->_bindingRules;
	}
	
	public function updateUIObject() {
		$this->_UIObjectDataAdapter->preUpdate ();
		foreach ( $this->_bindingRules as $rule ) {
			$this->_UIObjectDataAdapter->set ( $rule->getField2Rule (), $this->_businessObjectDataAdapter->get ( $rule->getField1Rule () ) );
		}
		$this->_UIObjectDataAdapter->postUpdate ();
	}
	
	public function updateBusinessObject() {
		$this->_businessObjectDataAdapter->preUpdate ();
		foreach ( $this->_bindingRules as $rule ) {
			$this->_businessObjectDataAdapter->set ( $rule->getField1Rule (), $this->_UIObjectDataAdapter->get ( $rule->getField2Rule () ) );
		}
		$this->_businessObjectDataAdapter->postUpdate ();
	}
	
	static public function update($source, $sourceDataAdapter, $consumer, $consumerDataAdapter, $bindingRules) {
		$dataBinder = new self ( $source, $sourceDataAdapter, $consumer, $consumerDataAdapter, $bindingRules );
		$dataBinder->updateUIObject ();
		return $dataBinder->getUIObject ();
	}
}
